package cn.batch.daemon.loadBatch;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import cn.batch.daemon.Configuration;
import cn.batch.daemon.bean.ApplicantLoanInfo;
import cn.batch.daemon.bean.ApplicantPbocCcInfo;
import cn.batch.daemon.bean.ApplicantPbocInfo;
import cn.batch.daemon.bean.ApplicantPbocLoanInfo;
import cn.batch.daemon.bean.ApplicantionDecisionResult;
import cn.batch.daemon.bean.ApplicantionInfo;
import cn.batch.daemon.bean.IN_PreApproval;
import cn.batch.daemon.bean.PreparApprovalxmlTool;
import cn.batch.daemon.bean.TaskMissionBean;
import cn.batch.daemon.bean.TriggerRule;
import cn.batch.daemon.blazeServer.RiskWarningServer;
import cn.batch.daemon.hibernate.bean.PbocLoan;
import cn.batch.daemon.hibernate.bean.PbocLoanContractinfo;
import cn.batch.daemon.hibernate.bean.PbocLoanCurroverdue;
import cn.batch.daemon.hibernate.bean.PbocLoanLatest24m;
import cn.batch.daemon.hibernate.bean.PbocLoancard;
import cn.batch.daemon.hibernate.bean.PbocLoancardCurroverdue;
import cn.batch.daemon.hibernate.bean.PbocSummary;
import cn.batch.daemon.jdbc.MessionTaskStore;
import cn.batch.daemon.jdbc.RiskWarningStore;
import cn.batch.daemon.service.RiskWarningService;
import cn.batch.daemon.taskEnum.MissionSts;
import cn.batch.daemon.taskEnum.TaskMissionType;
import cn.batch.daemon.util.Assert;
import cn.batch.daemon.util.ConstantParameter;
import cn.batch.daemon.util.DateUtil;
import cn.batch.daemon.util.ServerFileConfig;

import com.blazesoft.server.base.NdServerException;
import com.blazesoft.server.base.NdServiceException;
import com.blazesoft.server.base.NdServiceSessionException;
import com.blazesoft.server.local.NdLocalServerException;
import com.blazesoft.util.NdWrappedException;

public class PbocRiskWarnMonthRuleCall extends HibernateDaoSupport{
	private static final Log logger = LogFactory.getLog(RiskWarnMonthRuleCall.class);

	private Configuration config;
	private ApplicationContext ctx;

	private String flagDate;
	private int loadBatchSize;

	private String errorFileUrl;
	private String resultFileUrl;

	private RiskWarningService riskWarningService;
	private RiskWarningServer serverService;

	private boolean initialized = false;
	
	private static String loanFlag = "1234567ZDG";
	
	/**
	 * @param config
	 */
	public PbocRiskWarnMonthRuleCall(Configuration config) {
		super();
		this.config = config;
	}

	/**
	 * 初始化
	 */
	public void initialize() {
		
		if (flagDate == null) {
			throw new RuntimeException("请传入批次日期。格式：yyyyMMdd");
		}

		if (ctx == null) {
			ctx = new ClassPathXmlApplicationContext(new String[] { "applicationContext*.xml" });
		}

		riskWarningService = (RiskWarningService) ctx.getBean("riskWarningService");
		errorFileUrl = config.getSetting(ConstantParameter.RISKWARN_MONTH_ERROR_FILE_URL, "");
		resultFileUrl = config.getSetting(ConstantParameter.RISKWARN_MONTH_RESULT_FILE_URL, "");
		loadBatchSize = config.getSetting(ConstantParameter.RISKWARN_LOAD_BATCH_SIZE, 10000);
		File errorFileDir = new File(errorFileUrl);
		if (!errorFileDir.exists()) {
			errorFileDir.mkdirs();
		}

		initialized = true;
	}

	public void run() {
		long debugstimg = System.currentTimeMillis();
		initialize();
		logger.info("初始化花费时间为（毫秒）："+(System.currentTimeMillis()-debugstimg));
		Assert.state(initialized, "必须初始化完成才能运行");

		logger.info("开始执行风险预警(按月)批处理，批次日期为" + flagDate);

		// 待处理客户数
		int custCount = 0;
		// 触发规则客户数
		int riskWarningCount = 0;
		// 任务起始时间
		long startTime = System.currentTimeMillis();
		// 任务结束时间
		long endTime = 0;

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		TaskMissionBean tmb = new TaskMissionBean();
		try {
			// 1.更新(新增)批量任务数据-表APP_BATCH_INFO
			tmb.setBatch_no(flagDate); // 批次号
			tmb.setMissionTyp(TaskMissionType.FXYJM); // 任务类型
			tmb.setDisposeStartTm(new Timestamp(System.currentTimeMillis())); // 起始时间
			tmb.setMissionSts(MissionSts.DISPOSING); // 任务状态
			tmb.setRemark("开始调用blaze风险预警规则库。 请等待..."); // 备注
			MessionTaskStore.updateTaskMissionStatus(tmb);
			
			// 查询客户信息
			logger.info("开始查询客户数据。");
			custCount = RiskWarningStore.getRiskCustInfoMonthCount(flagDate);
			logger.info("获得待处理客户数 :" + custCount);
			
			serverService = (RiskWarningServer) RiskWarningServer.createServer(ServerFileConfig.SERVER_PATH);
			logger.info("规则库服务已创建成功。");
			
			int page = custCount / loadBatchSize;
			int lastPageCount = custCount % loadBatchSize;
			if (lastPageCount > 0) {
				page++;
			}
			
			List<IN_PreApproval> custList = new ArrayList<IN_PreApproval>();
			List<IN_PreApproval> resultList = null;
			List<String> rule = new ArrayList<String>(10000); // 保存个贷需要的信息
			List<String> errorList = new ArrayList<String>(500);
			int fromRow, toRow;
			for (int i = 0; i < page; i++) {
				fromRow = i * loadBatchSize;
				toRow = (i == (page - 1)) ? fromRow + lastPageCount : fromRow + loadBatchSize;
				
				custList = RiskWarningStore.getRiskCustInfoMonthPage(flagDate, fromRow, toRow);
				logger.info("本次加载客户数：" + custList.size() + "起止行:"+fromRow + "-"+toRow);
				logger.info("开始调用风险预警规则库(按月)。 ");
				
				resultList = triggerRule(custList, errorList);
				logger.info("触发规则客户数为： " + resultList.size());
				
				long temp = System.currentTimeMillis();
				save(resultList, rule);
				logger.info("保存本次触发规则客户花费时间"+(System.currentTimeMillis()-temp));
				logger.info("入库已完成。");
				riskWarningCount += resultList.size();
			}
			
			serverService.shutdown();
			custList.clear();
			logger.info("resultList++++++++++++++++++++++++++++++++++++++++++++++++++++++"+resultList);
			if(resultList!=null){
			    resultList.clear();
			}
			
			// 保存触发规则的客户的指定字段（个贷需要的字段）到文件中
			logger.info("开始写规则结果文件。");
			long s = System.currentTimeMillis();
			saveListToFile(rule);
			logger.info("写规则结果文件花费时间："+(System.currentTimeMillis()-s));
			rule.clear();
			logger.info("写规则结果文件结束。");
			
			logger.info("开始写处理错误文件。");
			long ss = System.currentTimeMillis();
			saveErrorList(errorList);
			logger.info("写处理错误文件花费时间"+(System.currentTimeMillis()-ss));
			errorList.clear();
			logger.info("写处理错误文件结束。");

			// 6.修改批量任务表(APP_BATCH_INFO)的任务状态、风险预警批处理表BATCH_CODE_LIST中的信息
			tmb.setMissionSts(MissionSts.SUCCESS);
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setRemark("本次风险预警（按月）批处理客户数为：" + custCount + "，触发规则客户数为：" + riskWarningCount + "，处理失败客户数为：" + errorList.size());
			MessionTaskStore.updateTaskMissionStatus(tmb);
		
			endTime = System.currentTimeMillis();
			long along = endTime - startTime;
			logger.info("****************************************");
			logger.info("*    本次风险预警(按月)批处理任务执行完成于： " + new java.util.Date());
			logger.info("*    本次风险预警(按月)批处理客户数为： " + custCount);
			logger.info("*    本次风险预警(按月)批触发规则客户数为： " + riskWarningCount);
			logger.info("*    本次风险预警(按月)批处理任务起始时间为： " + sdf.format(startTime));
			logger.info("*    本次风险预警(按月)批处理任务结束时间为： " + sdf.format(endTime));
			logger.info("*    共耗时 " + along + " 毫秒");
			logger.info("****************************************");
		} catch (Exception e) {
			tmb.setMissionSts(MissionSts.FAILED);
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setRemark("风险预警(按月)批量处理步失败。");
			try {
				MessionTaskStore.updateTaskMissionStatus(tmb);
			} catch (SQLException e2) {
				logger.error("更新任务状态失败。", e2);
			}

			logger.error("风险预警(按月)批量处理失败。", e);
			throw new RuntimeException(e);
		}
	}

	private void save(List<IN_PreApproval> resultList, List<String> rule) throws ParseException {
		List<ApplicantionInfo> appInfoCache = new ArrayList<ApplicantionInfo>(10000);
		for (int i = 0; i < resultList.size(); i++) {
			IN_PreApproval p = resultList.get(i);
			ApplicantionInfo appInfo = new ApplicantionInfo();
			appInfo.setBatchType(TaskMissionType.FXYJM.getValue());
			appInfo.setRecordDate(new Date());
			appInfo.setBatchDate(DateUtil.fromString(flagDate, "yyyyMMdd"));
			ApplicantLoanInfo applicantLoanInfo = p.getApplicantLoanInfo();
			appInfo.getApplicantLoanInfos().add(applicantLoanInfo);
			
			StringBuffer s = new StringBuffer();
			s.append(flagDate).append('|');
			s.append(applicantLoanInfo.getLoanAccount()).append('|');
			
			appInfo.getApplicantCmInfos().add(p.getApplicantCmInfo());
			appInfo.getApplicantSpouseInfos().add(p.getApplicantSpouseInfo());
			appInfo.getApplicantPbocInfos().add(p.getApplicantPbocInfo());
			ApplicantionDecisionResult applicantionDecisionResult = p.getApplicantionDecisionResult();
			appInfo.getApplicantionDecisionResults().add(applicantionDecisionResult);
			
			Set<TriggerRule> triggerRules = applicantionDecisionResult.getTriggerRules();
			for (TriggerRule tr : triggerRules) {
				s.append(tr.getRuleCode()).append(',');
			}
			s.deleteCharAt(s.length()-1);
			rule.add(s.toString());
			
			appInfoCache.add(appInfo);
		}
		riskWarningService.saveAll(appInfoCache);
	}

	private List<IN_PreApproval> triggerRule(List<IN_PreApproval> custList, List<String> errorList) {
		List<IN_PreApproval> resultList = new ArrayList<IN_PreApproval>();
		// 记录调用规则库时处理错误的申请信息的证件类型
		String idType = "";
		// 记录调用规则库时处理错误的申请信息的证件号码
		String idNo = "";
		long s0 = 0;
		for (int i = 0; i < custList.size(); i++) {
			s0 = System.currentTimeMillis();
			IN_PreApproval p = (IN_PreApproval) custList.get(i);
			idNo = p.getApplicantInfo().getIdNumber();
			idType = p.getApplicantInfo().getIdType();
			// 查询客户的征信数据
			if(idNo ==null || idType ==null){
				logger.info("客户号为："+p.getApplicantInfo().getCustNumber()+"客户的证件号码或证件类型为空");
				errorList.add("客户号为："+p.getApplicantInfo().getCustNumber()+"客户的证件号码或证件类型为空");
				continue;
			}
			PbocSummary ps = new PbocSummary();
			ps.setCertno(idNo);
			ps.setCerttype(idType);
			List<PbocSummary> list = riskWarningService.queryByExample(ps);
			ApplicantPbocInfo alpi = new ApplicantPbocInfo();
			ApplicantPbocCcInfo alpci = new ApplicantPbocCcInfo();
			ApplicantPbocLoanInfo alpli = new ApplicantPbocLoanInfo();
			if (list.size() > 0) {
				PbocSummary psummary = (PbocSummary) list.get(0);
				Set<PbocLoanLatest24m> pls = psummary.getPbocLoanLatest24ms();
				if (pls != null) {
					Iterator<PbocLoanLatest24m> it = pls.iterator();
					int[] mon6 = new int[pls.size()];
					int[] mon12 = new int[pls.size()];
					int m=0;
							while (it.hasNext()) {
								PbocLoanLatest24m plss = it.next();
								// 贷款最近6个月内最大逾期期数
								if (plss.getLatest24state() != null && !plss.getLatest24state().trim().equals("")) {
									String psM24 = plss.getLatest24state();
									int[] psM6 = new int[6];
									for (int i1 = 18; i1 < psM24.length(); i1++) {
										String s = psM24.charAt(i1) + "";
										if(loanFlag.contains(s)){
											if(s.equals("D")){
												psM6[i1 - 18] = 8;
											}else if(s.equals("Z")){
												psM6[i1 - 18] = 9;
											}else if(s.equals("G")){
												psM6[i1 - 18] = 10;
											}else{
												psM6[i1 - 18] = Integer.parseInt(s);
											}
										}else{
											psM6[i1 - 18] = 0;
										}
									}
									int m6 = 0;
									for (int i1 = 0; i1 < psM6.length; i1++) {
										if (m6 < psM6[i1]) {
											m6 = psM6[i1];
										}
									}
									mon6[m]=m6; 
								}
								// 贷款最近12个月内最大逾期期数
								if (plss.getLatest24state() != null && !plss.getLatest24state().trim().equals("")) {
									String psM24 = plss.getLatest24state();
									int[] psM12 = new int[12];
									for (int i1 = 12; i1 < psM24.length(); i1++) {
										String s = psM24.charAt(i1) + "";
										
										if(loanFlag.contains(s)){
											if(s.equals("D")){
												psM12[i1 - 12] = 8;
											}else if(s.equals("Z")){
												psM12[i1 - 12] = 9;
											}else if(s.equals("G")){
												psM12[i1 - 12] = 10;
											}else{
												psM12[i1 - 12] = Integer.parseInt(s);
											}
										}else{
											psM12[i1 - 12] = 0;
										}
									}
									int m12 = 0;
									for (int i1 = 0; i1 < psM12.length; i1++) {
										if (m12 < psM12[i1]) {
											m12 = psM12[i1];
										}
									}
									mon12[m]=m12;
								}
								m++;
							}
					int max6 = 0;
					for(int j=0;j<mon6.length;j++){
						if(max6<mon6[j]){
							max6 = mon6[j];
						}
					}
					p.getApplicantLoanInfo().setLoanOverdueTermMaxM6(
							new BigDecimal(max6));
					
					int max12 = 0;
					for(int j=0;j<mon12.length;j++){
						if(max12<mon12[j]){
							max12 = mon12[j];
						}
					}
					p.getApplicantLoanInfo().setLoanOverdueTermMaxM12(
							new BigDecimal(max12));
				}
				
				Set<PbocLoanContractinfo> plcs = psummary.getPbocLoanContractinfos();
				for (PbocLoanContractinfo plc : plcs) {
					// 贷款品种
					if (plc.getType() != null) {
						p.getApplicantLoanInfo().setLoanCategory(plc.getType());
					}
				}
				Set<PbocLoan> pbls = psummary.getPbocLoans();
				for (PbocLoan pbl : pbls) {
					// 当前贷款状态
					if (pbl.getState() != null) {
						alpli.setLoanStatus(pbl.getState());
					}
				}
				Set<PbocLoanCurroverdue> plcdues = psummary.getPbocLoanCurroverdues();
				for (PbocLoanCurroverdue plcdue : plcdues) {
					// 当前贷款的逾期的欠款金额
					if (plcdue.getCurroverdueamount() != null) {
						alpli.setLoanOverdraft(new BigDecimal(plcdue.getCurroverdueamount()));
					}
				}
				
				Set<PbocLoancard> plcards = psummary.getPbocLoancards();
				for (PbocLoancard plcard:plcards) {
					// 当前信用卡状态
					if (plcard.getState() != null) {
						alpci.setCcStatus(plcard.getState());
					}
				}
				
				Set<PbocLoancardCurroverdue> plccs = psummary.getPbocLoancardCurroverdues();
				for (PbocLoancardCurroverdue plcc:plccs) {
					// 当前信用卡逾期拖欠金额
					if (plcc.getCurroverdueamount() != null) {
						alpci.setCcOverdraft(new BigDecimal(plcc.getCurroverdueamount()));
					}
					
					// 当前信用卡逾期拖欠期数
					if (plcc.getCurroverduecyc() != null) {
						alpci.setCcOverdueTerm(plcc.getCurroverduecyc());
					}
				}

				p.setApplicantPbocInfo(alpi);
				p.getApplicantPbocInfo().getApplicantPbocLoanInfos().add(alpli);
				p.getApplicantPbocInfo().getApplicantPbocCcInfos().add(alpci);
			}
			// 组装XML
			String xml;
			try {
				xml = makeXML(p);
			} catch (Exception e1) {
				logger.error("组装客户信息为XML出错。", e1);
				errorList.add("组装: " + idNo + " -- " + idType + " -- 客户信息为XML时出错");
				continue; // 继续下一个客户
			}
			// 4.调用blaze规则库,得到规则处理结果,解析结果至IN_PreApproval对象中,将对象放入list
			String retVal;
			try {
				retVal = blazeRiskWarningServer(xml, serverService);
			} catch (Exception e) {
				logger.error("调用接口出错。", e);
				errorList.add(idNo + " -- " + idType + " -- 调用规则时出错");
				continue; // 继续下一个客户
			}
			// 解析返回的xml文件
			try {
				p = readRiskXml(retVal, p);
			} catch (Exception e) {
				logger.error("解析决策结果XML时出错。", e);
				errorList.add(idNo + " -- " + idType + " -- 解析决策结果XML时出错");
				continue; // 继续下一个客户
			}
			// 判断该用户有无触发blaze规则
			if (p != null) {
				// 将触发规则的客户信息及规则结果存储至数据库
				System.currentTimeMillis();
				resultList.add(p);
			}
			logger.info("每笔总耗时："+ (System.currentTimeMillis() - s0) );
		} // for end
		
		return resultList;
	}
	
	private void saveListToFile(List<String> r) throws IOException {
		FileOutputStream fos = null;
		OutputStreamWriter osw = null;
		BufferedWriter buffw = null;
		String pathname = "";
		try {
			pathname = resultFileUrl + flagDate + File.separator;
			File resultFile = new File(pathname + flagDate +".txt");
			File parentFile = resultFile.getParentFile();
			if (!parentFile.exists()) {
				parentFile.mkdirs();
			}
			fos = new FileOutputStream(resultFile);
			osw = new OutputStreamWriter(fos, Charset.forName("GBK"));
			buffw = new BufferedWriter(osw);
			for (String str : r) {
				buffw.write(str);
				buffw.newLine();
			}
			buffw.flush();
		} finally {
			if (buffw != null) {
				buffw.close();
			}
		}
		
		File flagFile = new File(pathname + flagDate + ".txt.OK");
		flagFile.createNewFile();
	}

	private void saveErrorList(List<String> errorList) {
		File file = new File(errorFileUrl + flagDate + ".txt");
		BufferedWriter bufferWriter = null;
		try {
			bufferWriter = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(file), "GBK"));
			for (String line : errorList) {
				bufferWriter.write(line);
				bufferWriter.newLine();
			}
			bufferWriter.flush();
		} catch (Exception e) {
			logger.error("保存处理失败数据出错。", e);
		} finally {
			try {
				bufferWriter.close();
			} catch (Exception e) {
				logger.error("关闭输出流失败。", e);
			}
		}
	}

	/**
	 * 组装blaze规则库所需指标的xml
	 * 
	 * @param p
	 * @return
	 * @throws IOException
	 */
	private String makeXML(IN_PreApproval p) throws IOException {
		return PreparApprovalxmlTool.writePreParApprovalXml(p);
	}

	/**
	 * 调用blaze规则库,得到规则处理结果
	 * 
	 * @param xml
	 * @return
	 * @throws NdLocalServerException
	 * @throws NdServerException
	 * @throws NdServiceException
	 */
	private String blazeRiskWarningServer(String xml,
			RiskWarningServer serverService) throws NdLocalServerException,
			NdServiceSessionException, NdServiceException, NdWrappedException {
		// 调用blaze规则服务
		return serverService.yjMEntry(xml);
	}

	/**
	 * 解析blaze规则处理后返回的xml结果
	 * 
	 * @param retVal
	 * @return
	 * @throws DocumentException
	 */
	private IN_PreApproval readRiskXml(String retVal, IN_PreApproval p)
			throws DocumentException {
		return PreparApprovalxmlTool.readRiskWarningXml(retVal, p);
	}

	/**
	 * @param flagDate
	 *            the flagDate to set
	 */
	public void setFlagDate(String flagDate) {
		this.flagDate = flagDate;
	}

}
